#include "aiPlane.h"
#include <cmath>
using namespace std;

#define SQR(a) ((a) * (a))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int SaveRound = 0;
/************************************************************************
 * 初始化函数：
 *     参数：
 *         name: Plane的名字
 *     说明：
 *         指定Plane的名字，做一些初始化工作。
 ***********************************************************************/
double doubleInf = 1e100;
bool judgeHit(const vector<Bullet>&, double, double, double, double, double, double& Min = doubleInf);

void init(string& name) {
	name = "Alyssa's plane";
}

// 获得子弹类型的函数。
int getBulletType(double vx, double vy) {
    for (int i = 0; i < 5; i ++)
        if (fabs(SQR(vx)+SQR(vy) - SQR(BULLET_V[i])) < EPSILON)
            return i;
    return -1;
}

// 判断在接下来的一个回合内，Plane是否会被击中。
// 这里是非常精确的判断，用到了一些初、高中几何学的知识。

bool judgeHit(const vector<Bullet>& bullets,
			double planeX, double planeY,
			double planeVx, double planeVy,
			double Time,double& Min ) {
	double xp = planeX, yp = planeY;
    double dx = planeVx, dy = planeVy;

    for (int i = 0; i < bullets.size(); i ++) {
        const Bullet& bullet = bullets[i];
        double xb = bullet.x, yb = bullet.y;
        double vx = bullet.vx, vy = bullet.vy;
        double a = SQR(vx - dx) + SQR(vy - dy);
        double b = 2*((xb - xp)*(vx - dx) + (yb - yp)*(vy - dy));
        int type = getBulletType(bullet.vx, bullet.vy);
        double c = SQR(xb - xp) + SQR(yb - yp) - SQR(BULLET_R[type]);
        double min = MIN(Time*Time*a + Time*b + c, c);
        double t = -b / (2*a);
        if (0 <= t && t <= Time)
            min = MIN(min, a*t*t + b*t + c);
		Min = MIN(Min,min);
        if (min <= EPSILON) return true;
    }

    return false;
}

/************************************************************************
 * 指定Plane动作的函数：
 *     参数：
 *         gameInfo: 当前游戏局面信息
 *         moves:    Plane要完成的移动
 *         skills:   Plane要使用的技能
 *         msg:      Plane要说的话，可以包括一些调试信息，会输出到录像中
 *     说明：
 *         根据当前游戏局面信息，指定Plane要做出的移动和使用的技能。
 ***********************************************************************/

namespace GreedyAndRandom
{

const int L = 360 , M = 10;
const double Pi = 3.1415926535897;
double Plane_v;
double bestX,bestY,MaxALLMin;
double xp,yp;
const double DELTA = 3;
double saveBx,saveBy,saveMin;
const double SuggestX = 950 ,SuggestY = 50 ;
const double ExperienceX = 4 , ExperienceY = 1;
inline double Rand(int L,int R)
{
	return rand()%(10*(R-L))/10.0;
}
void Try(const GameInfo& gameInfo,double vx,double vy,string& msg)
{

	double Min;
	double r = sqrt(vx*vx + vy*vy);
	vx = Plane_v * vx / r;
	vy = Plane_v * vy / r;
	if(xp+vx>1000){vx=1000-xp;msg+="x+ ";}
	if(xp+vx<0){vx=-xp;msg+="x- ";}
	if(yp+vy>1000){vy=1000-yp;msg+="y+ ";}
	if(yp+vy<0){vy=-yp;msg+="y- ";}

	bool t = judgeHit(gameInfo.bullets,xp,yp,vx,vy,3,Min);
	Min = Min / (log(fabs(yp+vy-50)+ExperienceX)) ;

	//char bufMove[1024];
	//sprintf(bufMove,"MaxALLMin is %lf %d %d ",MaxALLMin,bestX,bestY);

			//sprintf(bufMove, "Try (%lf, %lf) Min is %lf  judge is %d speed is %lf  ", vx, vy, Min,t,Plane_v);
	//msg += bufMove;
	if(!t&&(MaxALLMin < Min||MaxALLMin>=Min&&rand()>40000))
		{
			//char bufMove[1024];
			//sprintf(bufMove, "Try (%lf, %lf) Min is %lf  judge is %d speed is %lf    ", vx, vy, Min,t,Plane_v);
			//msg += bufMove;

			MaxALLMin = Min;
			bestX = vx;
			bestY = vy;
		}
}
void UpdateMoves(const GameInfo& gameInfo,vector<Move>& moves,int vx,int vy,vector<Skill>& skills,int skillType,string& msg)
{
	if(vx!=0&&vy!=0){
		Move move;
		move.vx = bestX;
		move.vy = bestY;
		move.startTime = gameInfo.round;
		move.endTime = gameInfo.round + 1;
		moves.push_back(move);
	}
	if(skillType==0){
		Skill skill;
		skill.startTime = gameInfo.round;
		skill.type = SPEEDUP;
		skills.push_back(skill);
		msg += " speedup ";
	}
	if(skillType==1){
		Skill skill;
		skill.startTime = gameInfo.round;
		skill.type = BOMB;
		skills.push_back(skill);
		msg += " bomb";
	}
	// 一些调试信息。
	char bufMove[1024];
	sprintf(bufMove, "(%lf, %lf) ", bestX, bestY);
	msg += bufMove;
	msg += "move";
}
void Main(const GameInfo& gameInfo, vector<Move>& moves, vector<Skill>& skills, string& msg)
{
	srand((unsigned)time(NULL));
	msg +="I'm Plane ";
	char bufRound[1024];
	sprintf(bufRound, "%d %d %lf %lf ", gameInfo.round, gameInfo.bullets.size(), gameInfo.planeX, gameInfo.planeY);
	msg = bufRound;
	msg += " ";

	if(gameInfo.round-SaveRound>1)
	{
		sprintf(bufRound, "%d %d error time out ", SaveRound ,gameInfo.round);
		msg = bufRound;
		msg += " ";
	}
	SaveRound = gameInfo.round;


	xp = gameInfo.planeX, yp = gameInfo.planeY;
	double tmp;

	// 如果停住不动就可以躲过这一回合的子弹，那么就停住。
	if (!judgeHit(gameInfo.bullets, xp, yp, 0, 0, 4,tmp)) {
		msg += "stay";
		return;
	}



	int direction;
	int speed;
	Plane_v = PLANE_V;
	MaxALLMin = -doubleInf;
	bestX = bestY = 0;
	for (Plane_v = PLANE_V;Plane_v>=15;Plane_v-=rand()%4+1){
	for (int i = -PLANE_V; i < PLANE_V; i ++ ){
		Try(gameInfo,40,i+Rand(0,1),msg);
		Try(gameInfo,-40,i+Rand(0,1),msg);
		Try(gameInfo,i+Rand(0,1),40,msg);
		Try(gameInfo,i+Rand(0,1),-40,msg);
	}

	}
	if  (MaxALLMin != -doubleInf){
		saveBx = bestX;
		saveBy = bestY;
		saveMin = MaxALLMin;
		if(MaxALLMin>DELTA) {
			UpdateMoves(gameInfo,moves,bestX,bestY,skills,-1,msg);
			msg+="way1 ";
			return;
		}
	}
	if(gameInfo.planeSkillsNum[0]>0){

	for(Plane_v=PLANE_V+1;Plane_v<=PLANE_V*2;Plane_v+=rand()%5+1){
		bestX = bestY = 0;
		Plane_v = PLANE_V *2;
		MaxALLMin = 1e100;
		for (int i = -PLANE_V; i < PLANE_V; i ++ ){
			Try(gameInfo,40,i+Rand(0,1),msg);
			Try(gameInfo,-40,i+Rand(0,1),msg);
			Try(gameInfo,i+Rand(0,1),40,msg);
			Try(gameInfo,i+Rand(0,1),-40,msg);
		}


	}
		if(MaxALLMin != -doubleInf && MaxALLMin>DELTA ) {
			UpdateMoves(gameInfo,moves,bestX,bestY,skills,0,msg);
			msg+="way2 ";
			return;
		}
	}

	if(gameInfo.planeSkillsNum[1]>0)
	{
		UpdateMoves(gameInfo,moves,0,0,skills,1,msg);
		msg+="way3 ";
		return;
	}
	else if(saveMin!=-doubleInf&&saveMin>=MaxALLMin){
		msg+="way4 ";
		UpdateMoves(gameInfo,moves,saveBx,saveBy,skills,-1,msg);
	}
	else if(MaxALLMin!=-doubleInf&&MaxALLMin>saveMin){
		msg+="way5 ";
		UpdateMoves(gameInfo,moves,bestX,bestY,skills,0,msg);
	}



	//else msg += " hit ";
}

}
void getAction(const GameInfo& gameInfo, vector<Move>& moves, vector<Skill>& skills, string& msg) {

	GreedyAndRandom::Main(gameInfo,moves,skills,msg);
}

